import traci

import traci.constants as tc

# 开启
ON = 1
# 关闭
OFF = 0
# 删除
DEL = -1
# 事故区监控距离
DEFAULT_DISTANCE = 20

# 事故区更新中（保证操作处于一个step中）
building = False

class AccidentDomain:

    def __new__(cls, *args, **kwargs):
        return super().__new__(cls)

    def __init__(self):
        self.accident_polygon = {}

    def set_new_accident(self, edge: str, lane_index: int, pos: list, switch=ON):
        """
        新增事故区 (str, int, double, list((double, double)), integer) -> None
        :param edge: 边缘
        :param lane_index: 道路序号
        :param pos: 区域坐标集合
        :param switch: 开关
        :return:
        """
        polygon_id = edge + '_' + str(lane_index)
        accident_item = _AccidentItem(polygon_id, edge, lane_index, pos, switch)
        self.accident_polygon[polygon_id] = accident_item
        # traci.lane.setAllowed(polygon_id, ['custom2'])
        traci.lane.setAllowed(polygon_id, ['authority'])
        # traci.lane.setDisallowed(polygon_id, ['passenger', 'custom1', 'bus', 'truck'])
        traci.edge.subscribeContext(edge, tc.CMD_GET_VEHICLE_VARIABLE, 20, [tc.VAR_LANE_INDEX, tc.VAR_TYPE])

    def get_accident_polygon(self, polygon_id: str):
        """
        获取指定事故区 (str) -> AccidentItem
        :param polygon_id: 事故区id
        :return: AccidentItem
        """
        return self.accident_polygon.get(polygon_id)

    def get_accident_polygon_list(self):
        """
        获取所有事故区
        :return: list(dict)
        """
        if len(self.accident_polygon) < 1:
            return []
        return list(map(lambda item: item[1].to_dict(), self.accident_polygon.items()))

    def control_accident(self, polygon_id: str, switch: int):
        """
        控制事故区 （str, int） -> int
        :param polygon_id: 区域id
        :param switch: -1：删除 0：关闭 1：开启
        :return: 0: 失败 1：成功
        """
        assert polygon_id in self.accident_polygon.keys(), '事故区不存在'
        result = 0
        accident_item = self.accident_polygon[polygon_id]
        if accident_item is not None and accident_item.switch != switch:
            if switch == OFF:
                self.accident_polygon[polygon_id].switch = 0
                traci.lane.setDisallowed(polygon_id, [])
                traci.edge.unsubscribeContext(accident_item.edge, tc.CMD_GET_VEHICLE_VARIABLE, DEFAULT_DISTANCE)
                result = 1
            elif switch == ON:
                self.accident_polygon[polygon_id].switch = 1
                # traci.lane.setAllowed(polygon_id, ['custom2'])
                traci.lane.setAllowed(polygon_id, ['authority'])
                traci.edge.subscribeContext(accident_item.edge, tc.CMD_GET_VEHICLE_VARIABLE, DEFAULT_DISTANCE,
                                            [tc.VAR_LANE_INDEX, tc.VAR_TYPE])
                result = 1
            elif switch == DEL:
                del self.accident_polygon[polygon_id]
                traci.lane.setDisallowed(polygon_id, [])
                traci.edge.unsubscribeContext(accident_item.edge, tc.CMD_GET_VEHICLE_VARIABLE, DEFAULT_DISTANCE)
                result = 1
        return result

    def simulation_control_accident(self, polygon_id: str, switch: int):
        """
        控制事故区 （str, int） -> int
        :param polygon_id: 区域id
        :param switch: -1：删除 0：关闭 1：开启
        :return: 0: 失败 1：成功
        """
        assert polygon_id in self.accident_polygon.keys(), '事故区不存在'
        result = 0
        accident_item = self.accident_polygon[polygon_id]
        if accident_item is not None and accident_item.switch != switch:
            if switch == OFF:
                self.accident_polygon[polygon_id].switch = 0
                traci.lane.setDisallowed(polygon_id, [])
                traci.edge.unsubscribeContext(accident_item.edge, tc.CMD_GET_VEHICLE_VARIABLE, DEFAULT_DISTANCE)
                result = 1
            elif switch == ON:
                self.accident_polygon[polygon_id].switch = 1
                # traci.lane.setAllowed(polygon_id, ['custom2'])
                traci.lane.setAllowed(polygon_id, ['authority'])
                traci.edge.subscribeContext(accident_item.edge, tc.CMD_GET_VEHICLE_VARIABLE, DEFAULT_DISTANCE,
                                            [tc.VAR_LANE_INDEX, tc.VAR_TYPE])
                result = 1
            elif switch == DEL:
                del self.accident_polygon[polygon_id]
                traci.lane.setDisallowed(polygon_id, [])
                traci.edge.unsubscribeContext(accident_item.edge, tc.CMD_GET_VEHICLE_VARIABLE, DEFAULT_DISTANCE)
                result = 1
        return result


class _AccidentItem:

    def __new__(cls, *args, **kwargs):
        return super().__new__(cls)

    def __init__(self, polygon_id, edge, lane_index, pos, switch=1):
        self.polygon_id = polygon_id
        self.edge = edge
        self.lane_index = lane_index
        self.pos = pos
        self.switch = switch

    def to_dict(self):
        return {
            'polygonId': self.polygon_id,
            'pos': self.pos,
            'edge': self.edge,
            'laneIndex': self.lane_index,
            'switch': self.switch
        }
